#include "Fragment_LinkLayer.h"
#include "Endian_utils.h"
#include "string.h"


static uint16_t Fragment_checksum(const uint8_t* data, uint16_t length) 
{
    if (data == NULL || length < 2) return 0;
    uint16_t sum_value = 0;
    for (uint16_t i = 0; i < length; i++) {
        sum_value += data[i];
    }
    return sum_value;
}

static uint16_t Fragment_GetSequence(void) 
{
    static uint16_t frame_id = 0;
    return frame_id++;
}

#include "stdio.h"
extern void hex_dump(const char* label, const void* data, size_t len);
// 帧解析函数
Fragment_LinkError_e Fragment_Frame_Parse(const uint8_t* in_data, uint16_t in_length, FrameBuff_t* out_Payload) 
{

    if (in_data == NULL || out_Payload == NULL) return LINK_ERR_FRAME_PARAM;
    if (in_length < MIN_FRAME_LENGTH || in_length > MAX_FRAME_LEN) return LINK_ERR_FRAME_PARAM;

    if ((in_data[0] != FRAME_HEADER_H) || (in_data[1] != FRAME_HEADER_L)) return LINK_ERR_FRAME_HEAD;
    if ((in_data[in_length - 2] != FRAME_TAIL_H) || (in_data[in_length - 1] != FRAME_TAIL_L)) return LINK_ERR_FRAME_TAIL;
    out_Payload->length = 0;
    uint16_t frame_length = read16_le(&in_data[2]);

    /* total length min is 10 */
    /* Frame structure: HEAD(2) + LENGTH(2) + FRAME_ID(2) + PAYLOAD(N) + CHECKSUM(2) + TAIL(2)*/
    /* exclude HEAD(2) + LENGTH(2) + CHECKSUM(2) + TAIL(2) == 8bytes */

    if ((frame_length != (in_length - 8)) && (frame_length > 0)) return LINK_ERR_FRAME_LENGTH;


    uint16_t read_checksum = read16_le(&in_data[in_length - 4]);
    uint16_t calc_checksum = Fragment_checksum(&in_data[2], in_length - 6); // exclude head, tail, checksum, 6bytes

    if (read_checksum != calc_checksum) return LINK_ERR_SUMCHECK;

    uint16_t sequence = read16_le(&in_data[4]);

    static uint16_t pre_sequence = 0xffff;
    if (pre_sequence == sequence) return LINK_ERR_REPEAT;
    pre_sequence = sequence;

    out_Payload->length = frame_length - 2;    // payload length is frame_length - 2 (for frame_id)


    memcpy(&out_Payload->arrData[0], &in_data[6], out_Payload->length);

    return LINK_SUCCESS;
}


/**
 *  will re_calc checksum, length and fill frame head
 */
uint16_t Fragment_Frame_Serialize(   FrameBuff_t *in_payload, uint8_t *output, uint16_t buf_size) 
{
    if((in_payload == NULL) || (output == NULL) || (buf_size < (in_payload->length + 10))) return 0;
    if (in_payload->length > FRAME_PAYLOAD_LEN) return 0;
    uint8_t *ptr = output;
    uint16_t remaining = buf_size;

    uint16_t index = 0;
    ptr[index] = FRAME_HEADER_H; // Frame header high byte
    index++;
    ptr[index] = FRAME_HEADER_L; // Frame header low byte
    index++;
    
    write16_le(&ptr[index], in_payload->length + 2);  // in_payload length + 2 (for frame_id)
    index += 2;

    write16_le(&ptr[index], Fragment_GetSequence());
    index += 2;

    memcpy(&ptr[index], &in_payload->arrData[0], in_payload->length);
    index += in_payload->length;

    // Checksum covers: [length + frame_id + in_payload]
    // exclude header(2) and tail(2)
    write16_le(&ptr[index], Fragment_checksum(&output[2], index - 2));  
    index += 2;

    ptr[index] = FRAME_TAIL_H; // Frame tail high byte
    index++;
    ptr[index] = FRAME_TAIL_L; // Frame tail low byte
    index++;
    return index;
}


